package cn.com.ethos.search.index;


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;

import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;

import cn.com.ethos.search.Constants;
import cn.com.ethos.search.query.ResultResponse;

public class IndexServer {

	/**
	 * default value for setMaxMergeDocs()
	 */
	private int maxMergeDocsNum = 10000*50;
	
	/**
	 * default value for setMergeFactor()
	 */
	private int mergeFactorNum  = 100;
	
	/**
	 * default value for setMaxBufferedDocs()
	 */
	private int maxBufferDocsNum = 1000;

	/**
	 * default value for setUseCompoundFile()
	 */
	private boolean useCompoundFile = false;
	
	private final Logger logger = Logger.getLogger(IndexServer.class);
	
	private Directory directory;
	private Analyzer analyzer;
	
	/**
	 * delete document
	 */
	private IndexModifier modifier;
	
	private boolean isCreate = false;
	
	/**
	 * @param path a directory path for create index
	 * @param analzyer Analyzer for analysis
	 * @param documentStream document provider 
	 * @throws CorruptIndexException 
	 * @throws LockObtainFailedException
	 * @throws IOException
	 */
	public IndexServer(Directory path,Analyzer analyzer,boolean create)
			throws IOException 	{
		this.directory = path;
		this.analyzer  = analyzer;
		this.isCreate  = create;
	}
	
	/**
	 * start index document from document stream
	 * 
	 * @throws CorruptIndexException
	 * @throws IOException
	 */
	public void start(IDocumentStream[] documentStreams)
			throws IOException {
		IndexWriter indexer = initIndexer(isCreate);
		
		long count = 0;
		for(IDocumentStream stream : documentStreams) {
			try{
				stream.open();
			} catch(Exception e) {
				logger.error("Open document stream error,please check log for detail," +e.getMessage(),e);
				break;
			}
			try {
				while (stream.hasNext() ){
					logger.debug("The " + (++count) + "file was indexed document:" + stream.next().toString().substring(0,120));
					logger.info(count);
					indexer.addDocument(stream.next());
				}
			} catch(Exception e) {
				logger.error("Retrive document from stream error,index was closed. check log for detail,"+e.getMessage(),e);
				break;
			} finally {
				stream.close();
			}
		}
		try {
			indexer.optimize();
			indexer.close();
			logger.info("\r\n-------------------------------------------");
			logger.info("\r\nThere are " + count + " files were indexed!");
		} catch (Exception e) {
			logger.error("Closing indexer error :" + e.toString());
		}
	}
	
	
	public void deleteDocument(Term term,IndexModifier modifier) throws IOException{
		modifier.deleteDocuments(term);
		modifier.close();
	}
	
	public void delete(InputStream stream) throws IOException{
		modifier = 
			null == modifier ? new IndexModifier(this.directory,new StandardAnalyzer(),false) : modifier;
		
		BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
		for(;;) {
			String line = null;
			try {
				line = reader.readLine();
			}catch(Exception ex) {
				/*** end of stream **/
				break;
			}			
			if(null == line || line.isEmpty()) {
				break;
			} else {
				String[] vals = line.split(":| ");				
				if(vals.length < 1) {
					logger.info("delete document failed from : " + line);
					continue;
				} else {
					Term t = new Term(vals[0],vals[1]);				
					this.modifier.deleteDocuments(t);
					logger.info("delete document successfully from : " + line);
				}
			}
		}
		this.modifier.close();
	}
	
	public void addDocument(Document doc,boolean close) 
			throws IOException {
		IndexWriter indexer = initIndexer(false);
		indexer.addDocument(doc);
		if(close == true) {
			indexer.optimize();
			indexer.close();
		}
	}
	public void addDocument (Document doc) 
			throws IOException {
		this.addDocument(doc,true);
	}
	
	public void optimize() throws IOException{
		IndexWriter indexer = initIndexer(false);
		indexer.optimize();
		indexer.close();
	}
	
	
	
	
	private IndexWriter initIndexer(boolean create) throws IOException {
		
		IndexWriter indexer = new IndexWriter(this.directory, analyzer, create);
		indexer.setMergeFactor(this.mergeFactorNum);
		indexer.setMaxMergeDocs(this.maxMergeDocsNum);
		indexer.setMaxBufferedDocs(this.maxBufferDocsNum);
		indexer.setUseCompoundFile(this.useCompoundFile);
		indexer.setMaxFieldLength(3000);
		
		logger.debug("initialize index writer successfully,"
				+ "merger factor is "        + this.mergeFactorNum
				+ ",the merge docs is "      + this.maxMergeDocsNum
				+ ",the buffer docs is "     + this.maxBufferDocsNum  
				+ ",the useCompoundFile is " + String.valueOf(this.useCompoundFile)
				);
		return indexer;
	}

	public int getMaxMergeDocsNum() {
		return this.maxMergeDocsNum;
	}

	public void setMaxMergeDocsNum(int maxMergeDocsNum) {
		this.maxMergeDocsNum = maxMergeDocsNum;
	}

	public int getMergeFactorNum() {
		return this.mergeFactorNum;
	}

	public void setMergeFactorNum(int mergeFactorNum) {
		this.mergeFactorNum = mergeFactorNum;
	}

	public int getMaxBufferDocsNum() {
		return this.maxBufferDocsNum;
	}

	public void setMaxBufferDocsNum(int maxBufferDocsNum) {
		this.maxBufferDocsNum = maxBufferDocsNum;
	}

	public boolean isUseCompoundFile() {
		return this.useCompoundFile;
	}

	public void setUseCompoundFile(boolean useCompoundFile) {
		this.useCompoundFile = useCompoundFile;
	}
}
